﻿#region Copyright(C) 2010-2010 NCS Pte. Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2010 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME	    : iConnect .Net Application Framework
// COMPONENT ID	    : NCS.IConnect.Core.Wcf
// COMPONENT DESC   : Core components for WCF.	
//
// CREATED DATE/BY  : 18 Aug 2010 / Gao Ye
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using NCS.IConnect.Core.Instrumentation;
using NCS.IConnect.Core.Wcf.Properties;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

namespace NCS.IConnect.Core.ApplicationContexts.Wcf
{
    /// <summary>
    /// client side's message attach inspector.
    /// </summary>
    public class ContextAttachMessageInspector : IClientMessageInspector
    {
        /// <summary>
        /// Gets the exceptional actions.
        /// </summary>
        public IList<string> ExceptionalActions { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ContextAttachMessageInspector"/> class.
        /// </summary>
        public ContextAttachMessageInspector()
        {
            this.ExceptionalActions = new List<string>();
        }

        private void EnsureCannSerialize(ContextItemCollection context)
        {
            try
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(ContextItemCollection));
                using (MemoryStream stream = new MemoryStream())
                {
                    serializer.WriteObject(stream, context);
                }
            }
            catch
            {
                throw new SerializationException(ResourceUtility.Format(Resources.ExceptionContextCannotBeSerialized));
            }

        }

        /// <summary>
        /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
        /// </summary>
        /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
        /// <param name="correlationState">Correlation state data.</param>
        public void AfterReceiveReply(ref Message reply, object correlationState)
        { }


        /// <summary>
        /// Enables inspection or modification of a message before a request message is sent to a service.
        /// </summary>
        /// <param name="request">The message to be sent to the service.</param>
        /// <param name="channel">The  client object channel.</param>
        /// <returns>
        /// The object that is returned as the correlationState argument of the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message@,System.Object)"/> method. This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid"/> to ensure that no two correlationState objects are the same.
        /// </returns>
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            if (this.ExceptionalActions.Contains(request.Headers.Action))
            {
                return null;
            }

            ApplicationContext.Current.UICulture = CultureInfo.CurrentUICulture;
            ApplicationContext.Current.Culture = CultureInfo.CurrentCulture;
            ApplicationContext.Current.TimeZone = TimeZoneInfo.Local;

            var context = new ContextItemCollection();
            foreach (var contextItem in ApplicationContext.Current.ContextLocator.GetCurrentContext())
            {
                if (contextItem == null)
                {
                    continue;
                }
                if (!contextItem.IsLocal)
                {
                    context.Add(contextItem);
                }
            }
            if (request.Version.Envelope == EnvelopeVersion.Soap11 || request.Version.Envelope == EnvelopeVersion.Soap12)
            {
                EnsureCannSerialize(context);
                var typedHeader = new MessageHeader<ContextItemCollection>(context);
                var header = typedHeader.GetUntypedHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace);
                request.Headers.Add(header);

                if (null != ActivityContext.Current)
                {
                    MessageHeader<ActivityContext> activityContextHeader = new MessageHeader<ActivityContext>(ActivityContext.Current);
                    request.Headers.Add(activityContextHeader.GetUntypedHeader(ActivityContext.MessageHeaderName, Constants.Namespace));
                }
            }
            else
            {
                string contextXml = SerializationUtil.Serialize(context);
                HttpRequestMessageProperty requestProperty;
                if (!request.Properties.Keys.Contains(HttpRequestMessageProperty.Name))
                {
                    requestProperty = new HttpRequestMessageProperty();
                }
                else
                {
                    requestProperty = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
                }
                requestProperty.Headers.Add(ApplicationContext.ContextHttpHeaderName, contextXml);
            }
            return null;
        }
    }
}